package computacaografica;

import java.awt.image.BufferedImage;
import java.util.LinkedList;
import java.util.List;

public class FachadaLogicaCG {

	private static FachadaLogicaCG instanciaUnica;

	private List<Reta> listaDeRetas;

	private BufferedImage image;

	private BufferedImage imageViewPort;

	private List<Reta> retas;

	private List<Matriz> transformacoes;

	private FachadaLogicaCG() {
		this.listaDeRetas = new LinkedList<Reta>();
		this.transformacoes = new LinkedList<Matriz>();
		this.retas = new LinkedList<Reta>();
	}

	/*
	 * Retorna a instancia unica
	 */
	public static FachadaLogicaCG getInstance() {
		if (instanciaUnica == null)
			instanciaUnica = new FachadaLogicaCG();

		return instanciaUnica;
	}

	/*
	 * atualiza a imagem da fachada
	 */
	public void setImage(BufferedImage image) {
		this.image = image;
	}

	/*
	 * Retorna a imagem da fachada
	 */
	public BufferedImage getImage() {
		return this.image;
	}

	public void redesenha(int value) {
		for (Reta r : this.listaDeRetas) {
			r.drawPontoMedio(value, image);
		}
	}

	/*
	 * Desenha uma reta usando DDA
	 */
	public void retaDDA(int x0, int y0, int x1, int y1, int value) {
		PontoCart pA = new PontoCart(x0, y0);
		PontoCart pB = new PontoCart(x1, y1);
		Reta r = new Reta(pA, pB);
		this.listaDeRetas.add(r);
		r.drawDDA(value, image);
	}

	/*
	 * Desenha uma reta usando Ponto Medio
	 */
	public void retaPontoMedio(int x0, int y0, int x1, int y1, int value) {
		PontoCart pA = new PontoCart(x0, y0);
		PontoCart pB = new PontoCart(x1, y1);
		Reta r = new Reta(pA, pB);
		this.listaDeRetas.add(r);
		r.drawPontoMedio(value, image);
	}

	/*
	 * Desenha a circunferencia usando a equcao explicita
	 */
	public void circExplicita(int raio, int value) {
		Circunferencia c = new Circunferencia(raio);
		c.drawEqExp(value, image);
	}

	/*
	 * Desenha a circunferencia usando o metodo trigonometrico
	 */
	public void circTrigonometrica(int raio, int value) {
		Circunferencia c = new Circunferencia(raio);
		c.drawTrigonomet(value, image);
	}

	/*
	 * Desenha a circunferencia usando o metodo do ponto medio
	 */
	public void circPontoMedio(int raio, int value) {
		Circunferencia c = new Circunferencia(raio);
		c.drawPontoMedio(value, image);
	}

	/*
	 * Desenha a elipse
	 */
	public void elipse(int a, int b, int value) {
		Elipse e = new Elipse(a, b);
		e.drawPontoMedio(value, image);
	}

	public void translata(double tx, double ty) {
		Translacao2D t = new Translacao2D(tx, ty);
		for (int i = 0; i < this.listaDeRetas.size(); i++) {
			this.listaDeRetas.set(i, this.listaDeRetas.get(i).transforma(t));
		}
		this.redesenha(20);
	}

	public void escala(double sx, double sy) {
		int tx = this.getXminimo();
		int ty = this.getYMinimo();
		Translacao2D tO = new Translacao2D(-tx, -ty);
		Scala2D s = new Scala2D(sx, sy);
		Translacao2D tD = new Translacao2D(tx, ty);
		for (int i = 0; i < this.listaDeRetas.size(); i++) {
			this.listaDeRetas.set(i, this.listaDeRetas.get(i).transforma(tO));
			this.listaDeRetas.set(i, this.listaDeRetas.get(i).transforma(s));
			this.listaDeRetas.set(i, this.listaDeRetas.get(i).transforma(tD));
		}
		this.redesenha(20);
	}

	public void rotaciona(double ang) {
		Rotacao2D r = new Rotacao2D(ang);
		for (int i = 0; i < this.listaDeRetas.size(); i++) {
			this.listaDeRetas.set(i, this.listaDeRetas.get(i).transforma(r));
		}
		this.redesenha(20);
	}

	public void refleteEmX() {
		Reflexao2D rX = new Reflexao2D(1);
		for (int i = 0; i < this.listaDeRetas.size(); i++) {
			this.listaDeRetas.set(i, this.listaDeRetas.get(i).transforma(rX));
		}
		this.redesenha(20);

	}

	public void refleteEmY() {
		Reflexao2D rY = new Reflexao2D(-1);
		for (int i = 0; i < this.listaDeRetas.size(); i++) {
			this.listaDeRetas.set(i, this.listaDeRetas.get(i).transforma(rY));
		}
		this.redesenha(20);

	}

	public void refleteEmReta(double a, double b) {
		Translacao2D t1 = new Translacao2D(0, -b);
		Rotacao2D r1 = new Rotacao2D(Math.toDegrees(Math.atan(a)));
		Reflexao2D rX = new Reflexao2D(1);
		Rotacao2D r2 = new Rotacao2D(Math.toDegrees(Math.atan(-a)));
		Translacao2D t2 = new Translacao2D(0, b);
		for (int i = 0; i < this.listaDeRetas.size(); i++) {
			this.listaDeRetas.set(i, this.listaDeRetas.get(i).transforma(t1));
			this.listaDeRetas.set(i, this.listaDeRetas.get(i).transforma(r1));
			this.listaDeRetas.set(i, this.listaDeRetas.get(i).transforma(rX));
			this.listaDeRetas.set(i, this.listaDeRetas.get(i).transforma(r2));
			this.listaDeRetas.set(i, this.listaDeRetas.get(i).transforma(t2));
		}
		this.redesenha(20);

	}

	public void cisalha(double cx, double cy) {
		int tx = this.getXminimo();
		int ty = this.getYMinimo();
		Translacao2D tO = new Translacao2D(-tx, -ty);
		Cisalhamento2D c = new Cisalhamento2D(cx, cy);
		Translacao2D tD = new Translacao2D(tx, ty);
		for (int i = 0; i < this.listaDeRetas.size(); i++) {
			this.listaDeRetas.set(i, this.listaDeRetas.get(i).transforma(tO));
			this.listaDeRetas.set(i, this.listaDeRetas.get(i).transforma(c));
			this.listaDeRetas.set(i, this.listaDeRetas.get(i).transforma(tD));
		}
		this.redesenha(20);
	}

	public void setImageViewPort(BufferedImage image) {
		this.imageViewPort = image;
	}

	public BufferedImage getImageViewPort() {
		return this.imageViewPort;
	}

	public String addTranslacao(double tx, double ty) {
		Translacao2D trans = new Translacao2D(tx, ty);
		this.transformacoes.add(trans);
		return trans.toString();
	}

	public String addScala(double sx, double sy) {
		int tx = this.getXminimo();
		int ty = this.getYMinimo();
		Translacao2D tO = new Translacao2D(-tx, -ty);
		Translacao2D tD = new Translacao2D(tx, ty);
		Scala2D scala = new Scala2D(sx, sy);
		this.transformacoes.add(tO);
		this.transformacoes.add(scala);
		this.transformacoes.add(tD);
		return scala.toString();

	}

	public String addRotaciona(double ang) {
		Rotacao2D rot = new Rotacao2D(ang);
		this.transformacoes.add(rot);
		return rot.toString();
	}

	public String addRefleteEmX() {
		Reflexao2D refle = new Reflexao2D(1);
		this.transformacoes.add(refle);
		return refle.toString();
	}

	public String addRefleteEmY() {
		Reflexao2D refle = new Reflexao2D(-1);
		this.transformacoes.add(refle);
		return refle.toString();
	}

	public String addCisalha(double cx, double cy) {
		int tx = this.getXminimo();
		int ty = this.getYMinimo();
		Translacao2D tO = new Translacao2D(-tx, -ty);
		Translacao2D tD = new Translacao2D(tx, ty);
		this.transformacoes.add(tO);
		Cisalhamento2D cisalha = new Cisalhamento2D(cx, cy);
		this.transformacoes.add(cisalha);
		this.transformacoes.add(tD);
		return cisalha.toString();
	}

	public void executeAllTransformations() {
		for (int i = 0; i < this.listaDeRetas.size(); i++) {
			for (int j = 0; j < this.transformacoes.size(); j++) {
				this.listaDeRetas.set(i, this.listaDeRetas.get(i).transforma(
						this.transformacoes.get(j)));
			}
		}
		this.redesenha(20);
		this.transformacoes.clear();
	}

	public void limpaTela() {
		this.listaDeRetas.clear();

	}

	public void limpaTransf() {
		this.retas.clear();
	}

	private int getXminimo() {
		int x = 0;
		if ((this.listaDeRetas != null) && (!this.listaDeRetas.isEmpty())) {
			x = this.listaDeRetas.get(0).getX0();
		}
		return x;
	}

	private int getYMinimo() {
		int y = 0;
		if ((this.listaDeRetas != null) && (!this.listaDeRetas.isEmpty())) {
			y = this.listaDeRetas.get(0).getY0();
		}
		return y;
	}

}
